package alibaba

import (
	"fmt"
	"strings"

	"github.com/bmatcuk/doublestar/v4"
)

// AGitFlowDetector is used to detect if the ref matches agitflow
type AGitFlowDetector interface {
	IsAGitFlow(string) bool
	IsTags(string) bool
	IsHeads(string) bool
	IsMergeRequests(string) bool
}

// NullDetector struct
type NullDetector struct{}

// IsAGitFlow detect if a ref is agit-flow
func (*NullDetector) IsAGitFlow(ref string) bool      { return false }
func (*NullDetector) IsTags(ref string) bool          { return false }
func (*NullDetector) IsHeads(ref string) bool         { return false }
func (*NullDetector) IsMergeRequests(ref string) bool { return false }

var _ AGitFlowDetector = (*GlobsDetector)(nil)

// GlobsDetector implements AGitFlowDetector using passed in globs
type GlobsDetector struct {
	agitGlobs          []string
	tagsGlobs          []string
	headsGlobs         []string
	mergeRequestsGlobs []string
}

// NewGlobsDetector creates a new GlobsDetector
func NewGlobsDetector(agitPatterns, tagsPatterns, headsPatterns, mergeRequestsPatterns []string) (*GlobsDetector, error) {
	agitGlobs, err := resolveGlobs(agitPatterns)
	if err != nil {
		return nil, err
	}
	tagGlobs, err := resolveGlobs(tagsPatterns)
	if err != nil {
		return nil, err
	}
	branchGlobs, err := resolveGlobs(headsPatterns)
	if err != nil {
		return nil, err
	}
	mergeRequestsGlobs, err := resolveGlobs(mergeRequestsPatterns)
	if err != nil {
		return nil, err
	}
	return &GlobsDetector{
		agitGlobs:          agitGlobs,
		tagsGlobs:          tagGlobs,
		headsGlobs:         branchGlobs,
		mergeRequestsGlobs: mergeRequestsGlobs,
	}, nil
}

// IsAGitFlow detect if a ref is agit-flow by a glob detector
func (d *GlobsDetector) IsAGitFlow(ref string) bool {
	for _, pattern := range d.agitGlobs {
		if isMatch, _ := doublestar.Match(pattern, ref); isMatch {
			return true
		}
	}
	return false
}

// IsTags detect if a ref is a tag by a glob detector
func (d *GlobsDetector) IsTags(ref string) bool {
	for _, pattern := range d.tagsGlobs {
		if isMatch, _ := doublestar.Match(pattern, ref); isMatch {
			return true
		}
	}
	return false
}

// IsHeads detect if a ref is a refs/heads by a glob detector
func (d *GlobsDetector) IsHeads(ref string) bool {
	for _, pattern := range d.headsGlobs {
		if isMatch, _ := doublestar.Match(pattern, ref); isMatch {
			return true
		}
	}
	return false
}

// IsMergeRequests detect if a ref is a refs/merge-requests by a glob detector
func (d *GlobsDetector) IsMergeRequests(ref string) bool {
	for _, pattern := range d.mergeRequestsGlobs {
		if isMatch, _ := doublestar.Match(pattern, ref); isMatch {
			return true
		}
	}
	return false
}

func resolveGlobs(patterns []string) ([]string, error) {
	globs := make([]string, 0, len(patterns))
	for _, pattern := range patterns {
		glob := strings.TrimSpace(pattern)
		if glob == "" {
			return nil, fmt.Errorf("empty pattern %q", pattern)
		}

		// some hack to follow git globs
		switch {
		case strings.HasSuffix(glob, "*"):
			// do nothing
		case strings.HasSuffix(glob, "/"):
			glob += "**"
		default:
			glob += "/**"
		}

		if doublestar.ValidatePattern(glob) {
			globs = append(globs, glob)
			continue
		}
	}
	return globs, nil
}
